package org.zhs.springframework.factory.xml;

import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.XmlUtil;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.zhs.springframework.BeansException;
import org.zhs.springframework.PropertyValue;
import org.zhs.springframework.core.io.Resource;
import org.zhs.springframework.core.io.ResourceLoader;
import org.zhs.springframework.factory.config.BeanDefinition;
import org.zhs.springframework.factory.config.BeanReference;
import org.zhs.springframework.factory.support.AbstractBeanDefinitionReader;
import org.zhs.springframework.factory.support.BeanDefinitionReader;
import org.zhs.springframework.factory.support.BeanDefinitionRegistry;

import java.io.IOException;
import java.io.InputStream;

public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {

    public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) {
        super(registry);
    }

    public XmlBeanDefinitionReader(BeanDefinitionRegistry registry, ResourceLoader resourceLoader){
        super(registry, resourceLoader);
    }


    @Override
    public void loadBeanDefinitions(Resource resource) throws BeansException {
        try {
            try(InputStream inputStream = resource.getInputStream()) {
                doLoadBeanDefinitions(inputStream);
            }
        }catch (IOException | ClassNotFoundException e){
            throw new BeansException("IOException parsing XML docment from" + resource, e);
        }
    }
    @Override
    public void loadBeanDefinitions(Resource... resources) throws BeansException {
        for (Resource resource : resources) {
            loadBeanDefinitions(resource);
        }
    }

    @Override
    public void loadBeanDefinitions(String location) throws BeansException {
        ResourceLoader resourceloader = getResourceloader();
        Resource resource = resourceloader.getResource(location);
        loadBeanDefinitions(resource);
    }

    protected void doLoadBeanDefinitions(InputStream inputStream) throws ClassNotFoundException {
        Document doc = XmlUtil.readXML(inputStream);
        Element root = doc.getDocumentElement();
        NodeList childNodes = root.getChildNodes();

        for (int i = 0; i < childNodes.getLength(); i++) {
            // 判断元素
            if( !(childNodes.item(i) instanceof Element) ){
                continue;
            }
            // 判断对象
            if( !"bean".equals(childNodes.item(i).getNodeName()) ){
                continue;
            }


            // 解析标签
            Element bean = (Element) childNodes.item(i);
            String id = bean.getAttribute("id");
            String name = bean.getAttribute("name");
            String className = bean.getAttribute("class");
            
            // 获取Class，方便获取类中的名称
            Class<?> clazz = Class.forName(className);
            // 优先级 id>name
            String beanName = StrUtil.isNotEmpty(id) ? id : name;
            if(StrUtil.isEmpty(beanName)){
                // 如果xml中的bean上没有id和name，就去获取该类的类名
                beanName = StrUtil.lowerFirst(clazz.getSimpleName());
            }

            // 定义 Bean 对象
            BeanDefinition beanDefinition = new BeanDefinition(clazz);
            // 读取属性并填充
            for (int j = 0; j < bean.getChildNodes().getLength(); j++) {
                // 判断元素
                if( !(bean.getChildNodes().item(j) instanceof Element) ){
                    continue;
                }
                // 判断对象
                if( !"property".equals(bean.getChildNodes().item(j).getNodeName()) ){
                    continue;
                }
                
                // 解析标签:property
                Element property = (Element) bean.getChildNodes().item(j);
                String attrName = property.getAttribute("name");
                String attrValue = property.getAttribute("value");
                String attrRef = property.getAttribute("ref");

                // 获取属性值： 引入对象、 值对象
                // 这里判断当前 property的有没有ref，如果有的话，表示当前值是另一个对象，所以需要new 一个BeanReference
                Object value = StrUtil.isNotEmpty(attrRef) ? new BeanReference(attrRef) : attrValue;

                // 创建属性信息
                PropertyValue propertyValue = new PropertyValue(attrName, value);
                beanDefinition.getPropertyValues().addPropertyValue(propertyValue);
            }

            // 判断当前对象的 bean信息是不是已经存在
            if(getRegistry().containsBeanDefinition(beanName)){
                throw new BeansException("Duplicate beanName[" + beanName + "] is not allowed");
            }

            // 注册 BeanDefinition
            getRegistry().registerBeanDefinition(beanName, beanDefinition);

        }
    }
}
